/*indexedDB*/
/*20210311 by 284949127*/
/*@author http://mmbro.gitee.com*/
/*@see https://www.cnblogs.com/ljwsyt/p/9760266.html*/
let indexDb = {
    defaultProp() {
        return {databaseName: 'defaultDB', tableName: 'defaultTbl', version: undefined, keyPath: 'cacheKey'};
    },
    /**
     * @param prop {} 数据库参数
     * @param sync bool 是否同步创建,默认false;为true则返回一个promise对象
     * @param isLog bool 是否打印日志,默认true;(生产的时候可以关掉)
     */
    new(prop = this.defaultProp(),sync = false,isLog = true) {
        let start=new Date().getTime();
        let cost=()=>new Date().getTime()-start;
        let log=(...msg)=>!isLog || console.log(...msg);
        let indexDb = {
            db: null,
            objectStore: null,
            request: null,
            isInit: false,
            prop: {databaseName: 'defaultDB', tableName: 'defaultTbl', version: undefined, keyPath: 'cacheKey'},
            init: async (prop = indexDb.prop) => {
                if (indexDb.isInit) {
                    log('do not init indexedDB again! indexedDB has already Initialized!');
                    return;
                }
                indexDb.prop = prop;
                const DriverName = 'indexedDB';
                if (!globalThis['indexedDB']) {
                    log('do not support indexedDB! indexedDB init failed!');
                    return;
                }
                let indexedDB = globalThis[DriverName];
                let request = indexedDB.open(indexDb.prop.databaseName, indexDb.prop.version);
                let isError = false;
                request.onerror = (event) => {
                    log(DriverName + ' open error!', event);
                    isError = true;
                };
                request.onsuccess = (event) => {
                    log(DriverName + ' open success!', event);
                    indexDb.db = request.result;
                };
                let p = new Promise((resolve) => {
                    request.onupgradeneeded = function (event) {
                        let db = event.target['result'];
                        // check objectStore name is exist and create objectStore
                        if (!db.objectStoreNames.contains(indexDb.prop.tableName)) {
                            log('create objectStore: ' + indexDb.prop.tableName);
                            indexDb.objectStore = db.createObjectStore(indexDb.prop.tableName, {keyPath: indexDb.prop.keyPath});
                        }
                        indexDb.db = db;
                        resolve();
                    };
                    setTimeout(() => resolve(), 500);
                });
                await p;
                indexDb.request = request;
                return indexDb.isInit = !isError;
            },
            add: async (cacheKey, obj) => {
                obj[indexDb.prop.keyPath] = cacheKey;
                // add if not exist
                let request = indexDb.db.transaction([indexDb.prop.tableName], 'readwrite')
                    .objectStore(indexDb.prop.tableName)
                    .add(obj);
                let p = new Promise(((resolve) => {
                    request.onsuccess = function (event) {
                        log('add success!', event);
                        resolve(true);
                    };
                    request.onerror = function (event) {
                        log('add failed!', event);
                        resolve(false);
                    }
                }));
                return await p;
            },
            get: async (cacheKey) => {
                let transaction = indexDb.db.transaction([indexDb.prop.tableName]);
                let objectStore = transaction.objectStore(indexDb.prop.tableName);
                let request = objectStore.get(cacheKey);
                let p = new Promise((resolve) => {
                    request.onerror = function (event) {
                        log('transaction read failed!', cacheKey, event);
                        resolve(undefined);
                    };
                    request.onsuccess = function (event) {
                        if (!!request.result) {
                            log('transaction read result!', cacheKey, request.result, event);
                            resolve(request.result);
                        } else {
                            log('transaction read empty!', cacheKey, event);
                            resolve(undefined);
                        }
                    };
                });
                let getObj = await p;
                log('getObj is', getObj);
                return getObj;
            },
            set: async (cacheKey, obj) => {
                obj[indexDb.prop.keyPath] = cacheKey;
                // update
                let request = indexDb.db.transaction([indexDb.prop.tableName], 'readwrite')
                    .objectStore(indexDb.prop.tableName)
                    .put(obj);
                let p = new Promise((resolve) => {
                    request.onsuccess = function (event) {
                        log('transaction update success!cacheKey=', cacheKey, event);
                        resolve(true);
                    };
                    request.onerror = function (event) {
                        log('transaction update failed!', event);
                        resolve(false);
                    };
                });
                return await p;
            },
            remove: async (cacheKey) => {
                let request = indexDb.db.transaction([indexDb.prop.tableName], 'readwrite')
                    .objectStore(indexDb.prop.tableName)
                    .delete(cacheKey);
                let p = new Promise((resolve) => {
                    request.onsuccess = function (event) {
                        log('transaction delete success!', event);
                        resolve(true);
                    };
                    request.onerror = function (event) {
                        log('transaction delete failed!', event);
                        resolve(false);
                    };
                });
                return await p;
            },
            readAll: async () => {
                let objectStore = indexDb.db.transaction(indexDb.prop.tableName).objectStore(indexDb.prop.tableName);
                let p = new Promise((resolve) => {
                    let results = {};
                    objectStore.openCursor().onsuccess = function (event) {
                        let cursor = event.target['result'];
                        if (cursor) {
                            results[cursor.key] = cursor.value;
                            cursor.continue();
                        } else {
                            log('cursor no more object! cursor:', cursor);
                            resolve(results);
                        }
                    };
                });
                return await p;
            },
            // @link https://www.zhihu.com/question/24227578
            search: async (key, value, keyFilter = 'main') => {
                let all = await indexDb.readAll();
                let searchResult = Object.keys(all).filter((oKey) => {
                    let obj = all[oKey];
                    let oKeyVal = obj[key];
                    if (!!oKeyVal && oKey.startsWith(keyFilter) && String(oKeyVal).includes(value)) {
                        return true;
                    }
                }).map((oKey) => all[oKey]);
                log('searchResult', searchResult);
                return searchResult;
            },
            // 不要readAll来搜索
            searchOne: async (key, value, keyFilter = 'main') => {
                let objectStore = indexDb.db.transaction(indexDb.prop.tableName).objectStore(indexDb.prop.tableName);
                let p = new Promise((resolve) => {
                    objectStore.openCursor().onsuccess = function (event) {
                        let cursor = event.target['result'];
                        if (cursor) {
                            let oKey = cursor.key;
                            let obj = cursor.value;
                            let oKeyVal = obj[key];
                            if (!!oKeyVal && oKey.startsWith(keyFilter) && String(oKeyVal).includes(value)) {
                                log('searchResult one', obj);
                                resolve(obj);
                            }else{
                                cursor.continue();
                            }
                        } else {
                            log('cursor no more object! cursor:', cursor);
                            resolve(null);
                        }
                    };
                });
                return await p;
                log('searchResult', searchResult);
                return searchResult;
            },
        };
        let p = indexDb.init(prop);
        if(sync){
            return new Promise(r=>p.then(()=>console.log(`indexedDB initialized ${cost()}ms ${prop.databaseName}-${prop.tableName}`,r(indexDb))))
        }
        p.then(() => console.log(`indexedDB initialized ${cost()}ms ${prop.databaseName}-${prop.tableName}`));
        return indexDb;
    }
};